#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>

#include "net.h"
#include "benc.h"
#include "metainfo.h"
#include "tracker.h"
#include "torrent.h"
#include "peer.h"

extern BOOL g_exit;

void *xx_tracker_handler(void * arg)
{
	int				 ret        = 0;
	xx_torrent		*torrent    = NULL;
	time_t			 t_beg      = 0; 
	time_t			 t_end		= 0;
	int				 time_elaps = torrent->tracker->interval + 1;

	if ( NULL == arg ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return NULL;
	}

	torrent = (xx_torrent *)arg;

	t_beg = time(NULL);

	while ( !g_exit ) {
		if (time_elaps > torrent->tracker->interval) {
			t_beg = t_end;
			if ( torrent->tracker->sockfd ) {
				xx_tracker_request(torrent);
				ret = xx_tracker_recieve(torrent);
				if ( ret < 0 ) {
					exit(-1);
				}
			}
		}
		t_end = time(NULL);
		time_elaps = t_end - t_beg;
		usleep(3000);
	}

	return ((void *)0);
}

int xx_tracker_init(xx_torrent *torrent)
{
	xx_tracker *tracker = torrent->tracker;		// fuck you again and again

	if ( NULL == torrent ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return -1;
	}

	if ( torrent->tracker == NULL ) {
		torrent->tracker = (xx_tracker *)malloc(sizeof(xx_tracker));
		if ( torrent->tracker == NULL ) {
			xx_err("init tracker error\n");
			exit(-1);
		}

		memset(torrent->tracker, 0, sizeof(xx_tracker));
	}

	tracker = torrent->tracker;

	while ( get_tracker_addr(torrent) != 2 ) {
		tracker->sockfd = xx_connect_host(tracker->host, tracker->port, tracker->type, NULL);
		if ( tracker->sockfd < 0) {
			continue;
		}
		break;
	}

	if ( tracker->sockfd <= 0) {
		return -1;
	}

	tracker->compact = 1;
	tracker->numwant = 50; 
	tracker->interval = 3; 
	tracker->min_interval = 2; 


	return 0;
}

int xx_tracker_request(xx_torrent *torrent)
{
	char		send_buf[MAX_TRACKER_LEN] = {0};

	if ( NULL == torrent ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return -1;
	}

	memset(send_buf, 0, MAX_TRACKER_LEN);
	sprintf(send_buf, 
			"GET %s?"
			"info_hash=%s&" "peer_id=%s&"
			"port=%d&"
			"uploaded=%d&" "downloaded=%d&" "left=%d&"
			"compact=%d&" "no_peer_id=%d&"
//			"ip=%s&" "numwant=%s&" "key=%s&" "trackerid=%s&"
			"event=%s "
			"HTTP/1.1\r\n" 
			"Host: %s\r\n"
			"User-Agent: Transmission/1.0\r\n"
			"Connection: close\r\n\r\n",
			
			"/announce", 
			torrent->info_hash,/*"%07%05%37%61%97%9d%09%de%ad%94%d0%9e%83%26%db%91%97%97%b0%78",*/ torrent->peer_id,
			torrent->listen_port,
			torrent->uploaded, torrent->downloaded, torrent->left,
			torrent->tracker->compact, torrent->tracker->no_peer_id,
//			"ip=%s&" "numwant=%s&" "key=%s&" "trackerid=%s&"
			torrent->event,
			"torrent.ubuntu.com"
			);
	xx_dbg("sending: %s\n", send_buf);

	return xx_writen(torrent->tracker->sockfd, send_buf, strlen(send_buf));
}

int xx_tracker_recieve(xx_torrent *torrent)
{
	int				  len						= 0;
	int				  use_len					= 0; 
	int				  headlen					= 0;
	char			  recv_buf[MAX_TRACKER_LEN] = {0};
	benc			  dict						= {0};
	benc			**peer_list					= NULL;
	benc			**peer_dict					= NULL;
	char			  string[100]				= {0};
	int				  i							= 0;
	int				  str_len					= 0;
	char			* p							= NULL;
	int				  integer					= 0;
	int				  pos						= 0;
	char			  ip[16]					= {0};

	if ( NULL == torrent ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return -1;
	}

	memset(recv_buf, 0, MAX_TRACKER_LEN);
	len = xx_readn(torrent->tracker->sockfd, recv_buf, MAX_TRACKER_LEN);
	if ( len <= 0 ) {
		return len;
	}
	xx_dbg("recieve len: %d -- %s\n", len, recv_buf);

	if ( len < 12 || (memcmp(recv_buf, "HTTP/1.0 200", 12) != 0 && memcmp(recv_buf, "HTTP/1.1 200", 12) != 0)) {
		return -1;
	}

	p = strstr(recv_buf, "\r\n\r\n");

	if ( !p ) {
		return -1;
	}

	headlen = p - recv_buf + 4 ; 

	xx_dbg("buf %d: %s\n", headlen, &recv_buf[headlen]);
	benc_dict(&recv_buf[headlen] , len - headlen, &use_len, &dict);

	if ( 0 == get_benc_info("failure reason", BENC_STRING, &dict, string) ) {
		xx_err("----------tracker failed: %s\n", string);
		return -1;
	}

	if ( 0 == get_benc_info("warning message", BENC_STRING, &dict, string) ) {	// opt
		xx_dbg("tracker warning: %s\n", string);
	}
	get_benc_info("interval", BENC_INTEGER, &dict, &torrent->tracker->interval); 
	get_benc_info("min interval", BENC_INTEGER, &dict, &torrent->tracker->min_interval);
	get_benc_info("tracker id", BENC_STRING, &dict, torrent->tracker->tracker_id); 
	get_benc_info("complete", BENC_INTEGER, &dict, &torrent->complete);
	get_benc_info("incomplete", BENC_INTEGER, &dict, &torrent->incomplete);

	if ( get_benc_info("peers", BENC_LIST, &dict, &peer_list) ) {
		i = 0;
		peer_dict = (*peer_list)->b_elems;
		while ( *peer_dict ) {
			if ( BENC_DICT != (*peer_dict)->type ) {
				break;
			}

			if ( !torrent->tracker->compact ) {
				get_benc_info("peer id", BENC_STRING, *peer_dict, string); 
			}
			get_benc_info("ip", BENC_STRING, *peer_dict, ip); 
//			inet_aton(string, &torrent->peer_list[i].peer_addr.sin_addr);
			get_benc_info("port", BENC_INTEGER, *peer_dict, &integer); 
//			torrent->peer_list[i].peer_addr.sin_port = htons(integer);
			xx_peer_list_add(torrent->peer_list, string, ip, integer, &torrent->peer_num);

			i++;
			peer_dict++;
		}
	}
	else if ( get_benc_info("peers", BENC_STRING, &dict, string) ) {
		i = 0;
		str_len = strlen(string);
		while ( pos < str_len ){
//			xx_update_peer_list();
//			memcpy(&torrent->peer_list[i].peer_addr.sin_addr.s_addr, &string[pos], 4);
//			memcpy(&torrent->peer_list[i].peer_addr.sin_port, &string[pos + 4], 2);
			xx_peer_list_add2(torrent->peer_list, string[pos], string[pos + 4], &torrent->peer_num);

			i++;
			pos += 6;
		}
	}

	return 0;
}

// get next tracker addr 
int	get_tracker_addr(xx_torrent *torrent)
{
	char		 *ptr	   = NULL;
	char		 *p		   = NULL;
	char		 *announce = NULL; 
	char		**list	   = NULL;
	int			  type	   = 0;

	if ( NULL == torrent ) {
		xx_dbg("Input para Invalid\n");
		xx_assert(0);
		return -1;
	}

	if ( torrent->tracker->host && torrent->metainfo->announce_list) {
		list = torrent->metainfo->announce_list;
		while ( *list ){
			if ( strlen(*list) <= 7 ) {
				return -1;
			}

			if ( torrent->tracker->announce != torrent->metainfo->announce) {
				while ( torrent->tracker->announce != *list ) {
					list++;
					continue;
				}
				list++;
				if ( NULL == *list ) {
					return -2;
				}
			}

			torrent->tracker->announce = *list;
//			xx_dbg("tracker: %s\n", torrent->tracker->announce);

			if ( !strncmp(*list, "http://", 7) ) {
				type = 0;
			}else if ( !strncmp(*list, "udp://", 6) ) {
				type = 1;
			}else
				type = -1;

			announce = strdup(*list);
			if ( !announce ) {
				return -1;
			}

			ptr = strchr(announce + 7, ':');
			p = strstr(announce + 7, "/announce");
			if ( p ) {
				*p = '\0';
			}

			if ( ptr ) {	// tracker with port
				*ptr++ = '\0';
				p = ptr;
				while ( p && isdigit(*p) ){
					p++;
				}
				*p = '\0';
			}
			
			p = strstr(announce, "://");	// "http://" or "udp://"
			p += 3;
//			xx_dbg("host:%s port:%s\n", torrent->tracker->host, torrent->tracker->port ? torrent->tracker->port : "NULL");
//			xx_dbg("host:%s port:%s type:%d\n", p, ptr ? ptr : "NULL", type);
			if ( torrent->tracker->host ) {
				free(torrent->tracker->host);
			}
			torrent->tracker->host = strdup(p);

			if ( torrent->tracker->port ) {
				free(torrent->tracker->port);
				torrent->tracker->port = NULL;
			}
			if ( ptr ) {
				torrent->tracker->port = strdup(ptr);
			}
			torrent->tracker->type = type;

			free(announce);

			return 0;
		}

		if ( *list == NULL ) {
			return -2;
		}

	}
	else if (  torrent->metainfo->announce) { // first annouce
		if ( strlen(torrent->metainfo->announce) < 7 ) {
			return -1;
		}
		torrent->tracker->announce = torrent->metainfo->announce;

		if ( !strncmp(torrent->tracker->announce, "http://", 7) ) {
			torrent->tracker->type = 0;
		}else if ( !strncmp(torrent->tracker->announce, "udp://", 6) ) {
			torrent->tracker->type = 1;
		}else
			torrent->tracker->type = -1;

		announce = strdup(torrent->tracker->announce);
		if ( !announce ) {
			return -1;
		}
		ptr = strchr(announce + 7, ':');
		p = strstr(announce + 7, "/announce");
		if ( p ) {
			*p = '\0';
		}

		if ( ptr ) {	// tracker with port
			*ptr++ = '\0';
			p = ptr;
			while ( isdigit(*p) ){
				p++;
			}
			*p = '\0';
		}

		p = strstr(announce, "://");	// + 3 "http://" or "udp://"
		if ( !p ) {
			return -1;
		}
		torrent->tracker->host = strdup(p + 3);
		if ( ptr ) {
			torrent->tracker->port = strdup(ptr);
		}

		free(announce);
	}

	return 0;
}

